#include "world.h"
#include "background.h"
#include "context_ptr.h"
#include "examples.h"
#include "feature.h"
#include "output.h"
#include "result.h"
#include "scenario.h"
#include "step.h"
#include "step_definition.h"
#include "table.h"

using namespace CppStory;

static World* g_world = NULL;

World* World::instance()
{
    if (g_world == NULL)
        g_world = new World();
    return g_world;
}

void World::destroy()
{
    delete g_world;
    g_world = NULL;
}

World* World::create()
{
    return new World();
}

World::World()
: cur_feature(NULL)
, output(NULL)
, background_failed(false)
{
}

World::~World()
{
    std::vector< Feature* >::iterator feature_iter = features.begin();
    while (feature_iter != features.end())
    {
        delete *feature_iter;
#ifdef WIN32
        feature_iter = features.erase(feature_iter);
#else
        features.erase(feature_iter);
#endif
    }

    std::vector< StepDefinition* >::iterator step_def_iter = step_defs.begin();
    while (step_def_iter != step_defs.end())
    {
        delete *step_def_iter;
#ifdef WIN32
        step_def_iter = step_defs.erase(step_def_iter);
#else
        step_defs.erase(step_def_iter);
#endif
    }
}

void World::register_feature(Feature *f)
{
    features.push_back(f);
    cur_feature = f;
}

void World::register_step_def(StepDefinition *s)
{
    step_defs.push_back(s);
    cur_step_def = s;
}

bool World::match_step_defs()
{
    std::vector< Feature* >::iterator feature_iter;
    for (feature_iter = features.begin();
         feature_iter != features.end();
         ++feature_iter)
    {
        if (NULL != (*feature_iter)->get_background())
        {
            if (!match_step_defs_of_steps((*feature_iter)->get_background()->steps))
                return false;
        }
        if (!match_step_defs_of_feature(*feature_iter))
            return false;
    }
    return true;
}

bool World::match_step_defs_of_feature(Feature* feature)
{
    std::vector< Scenario* >::iterator scenario_iter;
    for (scenario_iter = feature->scenarios.begin();
         scenario_iter != feature->scenarios.end();
         ++scenario_iter)
    {
        if (!match_step_defs_of_scenario(*scenario_iter))
            return false;
    }
    return true;
}

bool World::match_step_defs_of_scenario(Scenario* scenario)
{
    if (!match_step_defs_of_steps(scenario->steps))
        return false;
    else
        return true;
}

bool World::match_step_defs_of_steps(std::vector< Step* > &steps)
{
    std::vector< Step* >::iterator step_iter;
    for (step_iter = steps.begin();
         step_iter != steps.end();
         ++step_iter)
    {
        std::vector< StepDefinition* >::iterator step_def_iter;
        for (step_def_iter = step_defs.begin();
             step_def_iter != step_defs.end();
             ++step_def_iter)
        {
            if ((*step_def_iter)->match(*step_iter, (*step_iter)->get_name()))
            {
                if (NULL != (*step_iter)->get_step_def())
                {
                    output->output_ambiguous_step(*step_iter, (*step_iter)->get_step_def(), *step_def_iter);
                    return false;
                }
                (*step_iter)->set_step_def(*step_def_iter);
            }
        }
    }
    return true;
}

void World::replace()
{
    std::vector< Feature* >::iterator feature_iter;
    for (feature_iter = features.begin();
         feature_iter != features.end();
         ++feature_iter)
    {
        std::vector< Scenario* >::iterator scenario_iter;
        for (scenario_iter = (*feature_iter)->scenarios.begin();
             scenario_iter != (*feature_iter)->scenarios.end();
             ++scenario_iter)
        {
            if (NULL != (*scenario_iter)->get_examples() && NULL != (*scenario_iter)->get_examples()->get_table())
            {
                std::vector< Step* >::iterator step_iter;
                for (step_iter = (*scenario_iter)->steps.begin();
                     step_iter != (*scenario_iter)->steps.end();
                     ++step_iter)
                {
                    Table* table = (*scenario_iter)->get_examples()->get_table();
                    for (int col = 0; col < table->get_col_count(); col++)
                    {
                        if ((*step_iter)->replace(table->get_head_name(col), table->get_data(0, col)))
                            break;
                    }
                }
            }
        }
    }
}

bool World::run(Output* out)
{
    if (NULL == out)
        return false;
    output = out;

    replace();
    if (!match_step_defs())
        return false;

    bool ret = true;
    std::vector< Feature* >::iterator feature_iter;
    for (feature_iter = features.begin();
         feature_iter != features.end();
         ++feature_iter)
    {
        Feature* feature = *feature_iter;
        if (feature->run())
            successful_features.push_back(feature);
        else
        {
            failed_features.push_back(feature);
            ret = false;
        }
    }

    output_features();
    output_summary();
    return ret;
}

void World::output_features()
{
    std::vector< Feature* >::iterator feature_iter;
    for (feature_iter = successful_features.begin();
         feature_iter != successful_features.end();
         ++feature_iter)
    {
        output_feature(*feature_iter);
    }
    for (feature_iter = failed_features.begin();
         feature_iter != failed_features.end();
         ++feature_iter)
    {
        output_feature(*feature_iter);
    }
}

void World::output_feature(Feature* feature)
{
    background_failed = false;
    output->output_feature(feature);
    if (NULL != feature->get_background())
    {
        output_background(feature->get_background());
        if (!feature->get_background()->get_result())
            background_failed = true;
    }

    output_scenarios(feature);
}

void World::output_background(Background* background)
{
    output->output_background(background);
    output_steps(background->steps);
}

void World::output_scenarios(Feature* feature)
{
    std::vector< Scenario* >::iterator scenario_iter;
    for (scenario_iter = feature->successful_scenarios.begin();
         scenario_iter != feature->successful_scenarios.end();
         ++scenario_iter)
    {
        output_scenario(*scenario_iter);
    }
    for (scenario_iter = feature->failed_scenarios.begin();
         scenario_iter != feature->failed_scenarios.end();
         ++scenario_iter)
    {
        output_scenario(*scenario_iter);
    }
}

void World::output_scenario(Scenario* scenario)
{
    output->output_scenario(scenario);
    output_steps(scenario->steps);
}

void World::output_steps(std::vector< Step* > &steps)
{
    bool skipped = false;

    if (background_failed)
        skipped = true;
    std::vector< Step* >::iterator step_iter;
    for (step_iter = steps.begin();
         step_iter != steps.end();
         ++step_iter)
    {
        if (!skipped)
        {
            if (!(*step_iter)->get_step_def())
            {
                output->output_step(*step_iter, NULL, RT_UNDEFINED);
                skipped = true;
            }
            else
            {
                RESULT_TYPE ret;
                if ((*step_iter)->get_step_def()->get_result(*step_iter))
                    ret = RT_SUCCESSFUL;
                else
                    ret = RT_FAILED;
                output->output_step(*step_iter, (*step_iter)->get_step_def(), ret);
                if (!(*step_iter)->get_step_def()->get_result(*step_iter))
                    skipped = true;
            }
        }
        else
        {
            if (!(*step_iter)->get_step_def())
                output->output_step(*step_iter, NULL, RT_UNDEFINED);
            else
                output->output_step(*step_iter, (*step_iter)->get_step_def(), RT_SKIPPED);
        }
    }
}

void World::output_summary()
{
    int scenario_success_num = 0;
    int scenario_failed_num = 0;
    std::vector< Feature* >::iterator feature_iter;
    for (feature_iter = successful_features.begin();
         feature_iter != successful_features.end();
         ++feature_iter)
    {
        scenario_success_num += (*feature_iter)->successful_scenarios.size();
        scenario_failed_num += (*feature_iter)->failed_scenarios.size();
    }
    for (feature_iter = failed_features.begin();
         feature_iter != failed_features.end();
         ++feature_iter)
    {
        scenario_success_num += (*feature_iter)->successful_scenarios.size();
        scenario_failed_num += (*feature_iter)->failed_scenarios.size();
    }

    output->output_scenario_summary(scenario_success_num+scenario_failed_num, scenario_failed_num);
    output->output_feature_summary(successful_features.size()+failed_features.size(),
                           failed_features.size());
}

